package projetofinal.wsdl2js.js;

import java.util.Iterator;

import org.apache.woden.wsdl20.Binding;
import org.apache.woden.wsdl20.BindingOperation;
import org.apache.woden.wsdl20.Description;
import org.apache.woden.wsdl20.ElementDeclaration;
import org.apache.woden.wsdl20.Endpoint;
import org.apache.woden.wsdl20.Service;
import org.apache.woden.wsdl20.TypeDefinition;
import org.apache.woden.wsdl20.extensions.soap.SOAPBindingExtensions;
import org.apache.woden.wsdl20.extensions.soap.SOAPBindingOperationExtensions;
import org.apache.woden.wsdl20.extensions.soap.SOAPConstants;
import org.apache.ws.commons.schema.XmlSchemaAll;
import org.apache.ws.commons.schema.XmlSchemaComplexType;
import org.apache.ws.commons.schema.XmlSchemaElement;
import org.apache.ws.commons.schema.XmlSchemaGroupBase;
import org.apache.ws.commons.schema.XmlSchemaObjectCollection;
import org.apache.ws.commons.schema.XmlSchemaSequence;
import org.apache.ws.commons.schema.XmlSchemaSimpleType;
import org.apache.ws.commons.schema.XmlSchemaSimpleTypeRestriction;
import org.apache.ws.commons.schema.XmlSchemaType;

import projetofinal.wsdl2js.exception.ExceptionHandler;
import projetofinal.wsdl2js.util.Args;
import projetofinal.wsdl2js.util.Consts;

@SuppressWarnings("serial")
public class JSFileStub extends JSFile{
	
	public JSFileStub(String aFolder, String aFileName, Description aDescription) {
		super(aFolder, aFileName);
		Args.checkForNull(aDescription);
		fDescription = aDescription;
	}
	
	public void toJavaScript () {
		//TODO Verificar se o description não é null
		createOrOverwriteFile();
		
		String targetNameSpace = fDescription.toElement().getTargetNamespace().toString();
		
		Service[] allServices = fDescription.getServices();
		
		//------------------Elements
		
		TypeDefinition[] allTypeDefinitions = fDescription.getTypeDefinitions();
		ElementDeclaration[] allElementDeclarations = fDescription.getElementDeclarations();
		
		
		writeln("var Schema = (function() {");
			writeln("var instance = {};");
			writeln("");
			
			writeln("var types = (function () {");
				writeln("var instance = {};");
			
				writeln("function WsdlObject(_name, _type, _value) {");
					writeln("var name = _name;");
					writeln("var type = _type;");
					writeln("var value = _value;");
					writeln("var attr = new Array();");
					writeln("var elementsArray = new Array();");
					writeln("");
					writeln("this.getName = getName;");
					writeln("this.getType = getType;");
					writeln("this.getValue = getValue;");
					writeln("this.getAttr = getAttr;");
					writeln("this.setName = setName;");
					writeln("this.setType = setType;");
					writeln("this.setValue = setValue;");
					writeln("this.setAttr = setAttr;");
					writeln("this.addElement = addElement;");
					writeln("");
					writeln("this.getAnAttr = getAnAttr;");
					writeln("this.setAnAttr = setAnAttr;");
					writeln("this.newAttr = newAttr;");
					writeln("");
					writeln("function getName() {");
						writeln("return name;");
					writeln("}");
					writeln("");
					writeln("function getType() {");
						writeln("return type;");
					writeln("}");
					writeln("");
					writeln("function getValue() {");
						writeln("return value;");
					writeln("}");
					writeln("");
					writeln("function getAttr() {");
						writeln("return attr;");
					writeln("}");
					writeln("");
					writeln("function setName(_name) {");
						writeln("name = _name;");
					writeln("}");
					writeln("");
					writeln("function setType(_type) {   ");
						writeln("type = _type;");
					writeln("}");
					writeln("");
					writeln("function setValue(_value) {   ");
						writeln("value = _value;");
					writeln("}");
					writeln("");
					writeln("function setAttr(_attr) {");
						writeln("attr = _attr;");
					writeln("}");
					writeln("");
					writeln("function addElement(_obj) {");
						writeln("elementsArray.push(_obj);");
					writeln("}");
					writeln("");
					writeln("function getAnAttr(_name, _pos) {");
						writeln("if (_pos == undefined) _pos = 0;");
						writeln("if (_pos > attr.length) throw new Error (500, " + Consts.DOUBLE_QUOTE + "Tentativa de recuperar uma posi��o inexistente" + Consts.DOUBLE_QUOTE + ");");
						writeln("if (_pos == attr.length) {");
							writeln("this.newAttr();");
						writeln("}");
						writeln("for (var p in attr[_pos]) {");
							writeln("if (_name == attr[_pos][p].getName()) {");
								writeln("return attr[_pos][p];");
							writeln("}");
						writeln("}");
					writeln("}");
					writeln("");
					writeln("function setAnAttr(_attr, _name, _pos) {");
						writeln("if (_pos == undefined) _pos = 0;");
						writeln("if (_pos > attr.length) throw new Error (500, " + Consts.DOUBLE_QUOTE + "Tentativa de fazer uma atribui��o fora da ordem." + Consts.DOUBLE_QUOTE + ");");
						writeln("if (_pos == attr.length) {");
							writeln("this.newAttr();");
						writeln("}");
						writeln("for (var p in attr[_pos]) {");
							writeln("if (_name == attr[_pos][p].getName()) {");
								writeln("attr[_pos][p] = _attr;");
							writeln("}");
						writeln("}");
					writeln("}");
					writeln("");
					writeln("function newAttr() {");
						writeln("var i = this.getAttr().length;");
						writeln("this.getAttr()[i] = new Array();");
						writeln("for (var j = 0; j < elementsArray.length; j++) {");
							writeln("this.getAttr()[i].push(elementsArray[j]);");
						writeln("}");
	//				this.getAttr()[i].push(new Elements.WsdlObject("sAString","String",null));
					writeln("}");		
				writeln("}");
				writeln("instance.WsdlObject = WsdlObject;");
				writeln("");
				for (int i = 0; i < allTypeDefinitions.length; i++) {
					wsdl2JsType((XmlSchemaType) allTypeDefinitions[i].getContent());
				}		
				writeln("return instance;");
			writeln("})();");
			writeln("");		
			writeln("instance.inheritFrom = types.WsdlObject;");
			writeln("instance.inheritFrom(" + Consts.DOUBLE_QUOTE + "schema" + Consts.DOUBLE_QUOTE + "," + Consts.DOUBLE_QUOTE + "Complex" + Consts.DOUBLE_QUOTE + ",undefined);");
			writeln("");
			for (int i = 0; i < allElementDeclarations.length; i++) { 
				wsdl2JsElement((XmlSchemaElement) allElementDeclarations[i].getContent());
			}
			writeln("return instance;");
		writeln("})();");
		
		writeln("");
		writeln("");
		
		//-----------------Operations
		
		//TODO modularizar as variáveis
		
		//TODO trocar o modelo em JSON por objeto function
		
		for (int i = 0; i < allServices.length; i++) {
			
			String serviceName = allServices[i].getName().getLocalPart().toString();
			writeln(serviceName + " = {");
			
			writeln("// namespace definition works on all services avaiable");
			writeln("// " + Consts.DOUBLE_QUOTE + "/definitions/@targetNamespace" + Consts.DOUBLE_QUOTE + "");
			writeln("namespace: " + Consts.DOUBLE_QUOTE + targetNameSpace + Consts.DOUBLE_QUOTE + ",");
			writeln("");
			
			Endpoint[] allEndpoints = allServices[i].getEndpoints();
			
			for (int j = 0; j < allEndpoints.length; j++) {
				
				String endPointName = allEndpoints[j].getName().toString();
				
				Binding binding = allEndpoints[j].getBinding();
				BindingOperation[] allBindingOperations = binding.getBindingOperations();
				SOAPBindingExtensions sbe = (SOAPBindingExtensions) binding.getComponentExtensionContext(binding.getType());
				
				String address = allEndpoints[j].getAddress().toString();
				
				writeln(endPointName + ": {");
				writeln("");				
					writeln("// Address for this specific service");
					writeln("// " + Consts.DOUBLE_QUOTE + "/definitions/service/port/soap:adress/@location" + Consts.DOUBLE_QUOTE + " ");
					writeln("address: " + Consts.DOUBLE_QUOTE + address + Consts.DOUBLE_QUOTE + ",");
				writeln("");
					writeln("soapVersion: " + Consts.DOUBLE_QUOTE + sbe.getSoapVersion() + Consts.DOUBLE_QUOTE + ",");
					writeln("// We need to define which binding is associated with this service");
					writeln("// " + Consts.DOUBLE_QUOTE + "/definitions/service/port/@binding" + Consts.DOUBLE_QUOTE + " == " + Consts.DOUBLE_QUOTE + "/definitions/binding/@name" + Consts.DOUBLE_QUOTE + "");
				writeln("");
					writeln("// An association between the binding chosen and a portType must be done to match the service operations ");
					writeln("// " + Consts.DOUBLE_QUOTE + "/definitions/binding/@type" + Consts.DOUBLE_QUOTE + " == " + Consts.DOUBLE_QUOTE + "/definitions/portType/@name" + Consts.DOUBLE_QUOTE + "");
				writeln("");
					writeln("// We have to build a function for each operation inside the binding and the portType");
					writeln("// ");
				writeln("");
					writeln("// The input and output parameters are located in each message defined at the operation tags input and output  ");
					writeln("// " + Consts.DOUBLE_QUOTE + "/definitions/portType/operation/input/@message" + Consts.DOUBLE_QUOTE + "");
					writeln("// " + Consts.DOUBLE_QUOTE + "/definitions/portType/operation/output/@message" + Consts.DOUBLE_QUOTE + "");
					writeln("// " + Consts.DOUBLE_QUOTE + "/definitions/message/part/@name" + Consts.DOUBLE_QUOTE + "");
				writeln("");
					writeln("// functionName:" + Consts.TAB + Consts.TAB + Consts.DOUBLE_QUOTE + "/definitions/binding/operation/@name" + Consts.DOUBLE_QUOTE + "");
					writeln("// body:" + Consts.TAB + Consts.TAB + Consts.TAB + Consts.TAB + Consts.DOUBLE_QUOTE + "/definitions/message/part/@name" + Consts.DOUBLE_QUOTE + "");
					writeln("// outMethod" + Consts.TAB + Consts.TAB + Consts.TAB + Consts.DOUBLE_QUOTE + "Input from the user" + Consts.DOUBLE_QUOTE + "");
					writeln("// operation: " + Consts.TAB + Consts.TAB + Consts.TAB + Consts.DOUBLE_QUOTE + "/definitions/binding/operation/@name" + Consts.DOUBLE_QUOTE + "");
					writeln("// action:" + Consts.TAB + Consts.TAB + Consts.TAB + Consts.TAB + Consts.DOUBLE_QUOTE + "/definitions/binding/operation/soap:operation/@soapAction" + Consts.DOUBLE_QUOTE + "");
					writeln("// outParam:" + Consts.TAB + Consts.TAB + Consts.TAB + Consts.DOUBLE_QUOTE + "New object for the particular function" + Consts.DOUBLE_QUOTE + "");
				writeln("");
				
				for (int k = 0; k < allBindingOperations.length; k++) {
					String interfaceOperationName = allBindingOperations[k].getInterfaceOperation().getName().getLocalPart();
					
						writeln(interfaceOperationName + ": function (body, outMethod) {");
							writeln("var operation = " + Consts.DOUBLE_QUOTE + interfaceOperationName + Consts.DOUBLE_QUOTE + ";");
							writeln("var output = " + Consts.DOUBLE_QUOTE + allBindingOperations[k].getInterfaceOperation().getInterfaceMessageReferences()[1].toElement().getElement().getQName().getLocalPart() + Consts.DOUBLE_QUOTE + ";");
								SOAPBindingOperationExtensions soapBindOperExts = (SOAPBindingOperationExtensions) allBindingOperations[k].getComponentExtensionContext(SOAPConstants.NS_URI_SOAP);
							writeln("var action = " + Consts.DOUBLE_QUOTE + soapBindOperExts.getSoapAction() + Consts.DOUBLE_QUOTE + ";");
							writeln("var outParam = Schema.getAnAttr(" + Consts.DOUBLE_QUOTE  + allBindingOperations[k].getInterfaceOperation().getInterfaceMessageReferences()[1].toElement().getElement().getQName().getLocalPart() + Consts.DOUBLE_QUOTE + ");");
							writeln("SOAPActions.call(" + serviceName + "." + endPointName + ".address, operation, output, outMethod, " + serviceName + ".namespace, action, true, body, outParam, " + serviceName + "." + endPointName + ".soapVersion);");
						writeln("},");
					writeln("");					
				}
				writeln("},");
			}
			writeln("}");
			writeln("");
		}		
		
		writeln("");
		writeln("");
		
		
		//---------SoapActions
		writeln("SOAPActions = {");
			writeln("");
			writeln("// Inicializa a requisição diferenciando uma operação síncrona de uma assíncrona através do retorno");
			writeln("call: function(location, operation, operationResponse, outMethod, namespace, soapaction, async, inParam, outParam, soapVersion) {");
				writeln("if(async) {");
					writeln("SOAPActions.loadMessage (location, operation, operationResponse, outMethod, namespace, soapaction, async, inParam, outParam, soapVersion);");
				writeln("} else {");
					writeln("return SOAPActions.loadMessage (location, operation, operationResponse, outMethod, namespace, soapaction, async, inParam, outParam, soapVersion);");
				writeln("}");
			writeln("},");
		writeln("");
			writeln("// Cria a mensagem, abrea conexão com o web service e envia a mensagem");
			writeln("loadMessage: function(location, operation, operationResponse, outMethod, namespace, soapaction, async, inParam, outParam, soapVersion) {");
			
			writeln("if (soapVersion = " + Consts.DOUBLE_QUOTE + "1.1" + Consts.DOUBLE_QUOTE + ") {");
				writeln("var message =");				
					writeln(Consts.DOUBLE_QUOTE + "<?xml version=" + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + "1.0" + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + " encoding=" + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + "utf-8" + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + "?>" + Consts.DOUBLE_QUOTE + " +");
					writeln(Consts.DOUBLE_QUOTE + "<soap:Envelope " +  Consts.DOUBLE_QUOTE + " +");
					writeln(Consts.DOUBLE_QUOTE + "xmlns:xsi=" + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + "http://www.w3.org/2001/XMLSchema-instance" + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + " " + Consts.DOUBLE_QUOTE + " +");
					writeln(Consts.DOUBLE_QUOTE + "xmlns:xsd=" + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + "http://www.w3.org/2001/XMLSchema" + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + " " + Consts.DOUBLE_QUOTE + " +");
					writeln(Consts.DOUBLE_QUOTE + "xmlns:soap=" + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + "http://schemas.xmlsoap.org/soap/envelope/" + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + ">" + Consts.DOUBLE_QUOTE + " +");
					writeln(Consts.DOUBLE_QUOTE + "<soap:Body>" + Consts.DOUBLE_QUOTE + " +");
					writeln(Consts.DOUBLE_QUOTE + "<" + Consts.DOUBLE_QUOTE + " + operation + " + Consts.DOUBLE_QUOTE + " xmlns=" + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + Consts.DOUBLE_QUOTE + " + namespace + " + Consts.DOUBLE_QUOTE + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + ">" + Consts.DOUBLE_QUOTE + " +");
					writeln("Utils.objectToXml(inParam) +");
					writeln(Consts.DOUBLE_QUOTE + "</" + Consts.DOUBLE_QUOTE + " + operation + " + Consts.DOUBLE_QUOTE + "></soap:Body></soap:Envelope>" + Consts.DOUBLE_QUOTE + ";");
			writeln("} else if (soapVersion = " + Consts.DOUBLE_QUOTE + "1.2" + Consts.DOUBLE_QUOTE + ") {");
				writeln("var message =");
					writeln(Consts.DOUBLE_QUOTE + "<?xml version=" + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + "1.0" + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + " encoding=" + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + "utf-8" + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + "?>" + Consts.DOUBLE_QUOTE + " +");
					writeln(Consts.DOUBLE_QUOTE + "<soap12:Envelope " +  Consts.DOUBLE_QUOTE + " +");
					writeln(Consts.DOUBLE_QUOTE + "xmlns:xsi=" + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + "http://www.w3.org/2001/XMLSchema-instance" + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + " " + Consts.DOUBLE_QUOTE + " +");
					writeln(Consts.DOUBLE_QUOTE + "xmlns:xsd=" + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + "http://www.w3.org/2001/XMLSchema" + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + " " + Consts.DOUBLE_QUOTE + " +");
					writeln(Consts.DOUBLE_QUOTE + "xmlns:soap12=" + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + "http://www.w3.org/2003/05/soap-envelope" + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + ">" + Consts.DOUBLE_QUOTE + " +");
					writeln(Consts.DOUBLE_QUOTE + "<soap12:Body>" + Consts.DOUBLE_QUOTE + " +");
					writeln(Consts.DOUBLE_QUOTE + "<" + Consts.DOUBLE_QUOTE + " + operation + " + Consts.DOUBLE_QUOTE + " xmlns=" + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + Consts.DOUBLE_QUOTE + " + namespace + " + Consts.DOUBLE_QUOTE + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + ">" + Consts.DOUBLE_QUOTE + " +");
					writeln("Utils.objectToXml(inParam) +");
					writeln(Consts.DOUBLE_QUOTE + "</" + Consts.DOUBLE_QUOTE + " + operation + " + Consts.DOUBLE_QUOTE + "></soap12:Body></soap12:Envelope>" + Consts.DOUBLE_QUOTE + ";");					
			writeln("}");
			writeln("else throw new Error (500, " + Consts.DOUBLE_QUOTE + "Vers�o SOAP para a constru��o da mensagem n�o identificada" + Consts.DOUBLE_QUOTE + ");");
			writeln("// Envia a requisição");
//				writeln("alert(message);");
					writeln("var xmlHttp = Utils.getXmlHttp(); ");
					writeln("xmlHttp.open(" + Consts.DOUBLE_QUOTE + "POST" + Consts.DOUBLE_QUOTE + ", location, async);");
					writeln("xmlHttp.setRequestHeader(" + Consts.DOUBLE_QUOTE + "SOAPAction" + Consts.DOUBLE_QUOTE + ", soapaction);");
					writeln("xmlHttp.setRequestHeader(" + Consts.DOUBLE_QUOTE + "Content-Type" + Consts.DOUBLE_QUOTE + ", " + Consts.DOUBLE_QUOTE + "text/xml; charset=utf-8" + Consts.DOUBLE_QUOTE + ");");
				writeln("if(async) {");
					writeln("xmlHttp.onreadystatechange = function() {");
						writeln("if(xmlHttp.readyState == 4) {");
							writeln("SOAPActions.loadedMessage(operation, operationResponse, outMethod, async, xmlHttp, outParam);");
						writeln("}");
					writeln("}");
				writeln("}");
				writeln("xmlHttp.send(message);");
				writeln("if (!async) {");
					writeln("return SOAPActions.loadedMessage(operation, operationResponse, outMethod, async, xmlHttp, outParam);");
				writeln("}");
			writeln("},");
		writeln("");
			writeln("// Quando o readyState for ok (4), Trabalhamos em cima da resposta e chamamos o método OutMethod que foi definido pelo usu�rio");
			writeln("loadedMessage: function(operation, operationResponse, outMethod, async, xmlHttp, outParam) {");
			writeln("// Aloca a resposta SOAP em uma estrutura de Nó utilizando a API DOM.");
//				writeln("alert (xmlHttp.responseText);");
				writeln("var ResponseNode = Utils.getNode(xmlHttp, operationResponse);");
				writeln("//Converte a estrutura de Nó para a estrutura de dados de outParam (Objeto)");
				writeln("outParam = Utils.nodeToObject(ResponseNode, outParam);");
				writeln("// E retorna a resposta");
				writeln("outMethod(outParam);");
				writeln("if(!async) {");
					writeln("return outParam;");
				writeln("}");
			writeln("}	");
		writeln("}");		
		
		
		writeln("");
		writeln("");
		
		
		//---------Utils
		
		writeln("Utils = {");
			writeln("");
			writeln("// checa se o objeto é um Array");
			writeln("isArray: function(object) {");
				writeln("return object != null && typeof object == " + Consts.DOUBLE_QUOTE + "object" + Consts.DOUBLE_QUOTE + " && 'splice' in object && 'join' in object;");
			writeln("},");
		writeln("");
			writeln("// verifica se o n� irm�o mais velho possui o mesmo nome do atual");
			writeln("samePreviousSibling: function(node) {");
				writeln("try {");
					writeln("return (node.nodeName == node.previousSibling.nodeName);");
				writeln("}");
				writeln("catch (ex) {}");
				writeln("return false;");
			writeln("},");
		writeln("");
			writeln("// verifica se o n� irm�o mais novo possui o mesmo nome do atual");
			writeln("sameNextSibling: function(node) {");
				writeln("try {");
					writeln("return (node.nodeName == node.nextSibling.nodeName);");
				writeln("}");
				writeln("catch (ex) {}");
				writeln("return false;");
			writeln("},");
		writeln("");
			writeln("// converte a estrutura de dados utilizada para tags XML");
			writeln("objectToXml: function (obj) {");
				writeln("xmlStr = " + Consts.DOUBLE_QUOTE + Consts.DOUBLE_QUOTE + ";");
				writeln("for (var i in obj.getAttr()[0]) {");
					writeln("xmlStr += Utils.buildTag (obj.getAttr()[0][i]);//t� feio");
				writeln("}");
				writeln("return xmlStr;");
			writeln("},");
		writeln("");
			writeln("// fun��o que complementa a anterior");
			writeln("buildTag: function (obj) {");
				writeln("var strTag = " + Consts.DOUBLE_QUOTE + Consts.DOUBLE_QUOTE + ";");
				writeln("if (obj.getType() == " + Consts.DOUBLE_QUOTE + "Complex" + Consts.DOUBLE_QUOTE + ") {");
					writeln("if (obj.getAttr().length > 0) {");
						writeln("strTag += " + Consts.DOUBLE_QUOTE + "<" + Consts.DOUBLE_QUOTE + " + obj.getName() + " + Consts.DOUBLE_QUOTE + ">" + Consts.DOUBLE_QUOTE + ";");
						writeln("for (var p in obj.getAttr()) {");
							writeln("for (var i in obj.getAttr()[p]) {");
								writeln("strTag += Utils.buildTag (obj.getAttr()[p][i]);");
							writeln("}");
						writeln("}");
						writeln("strTag += " + Consts.DOUBLE_QUOTE + "</" + Consts.DOUBLE_QUOTE + " + obj.getName() + " + Consts.DOUBLE_QUOTE + ">" + Consts.DOUBLE_QUOTE + ";");
					writeln("}");
				writeln("} else {");
					writeln("if (obj.getValue() != undefined) {");
						writeln("strTag += " + Consts.DOUBLE_QUOTE + "<" + Consts.DOUBLE_QUOTE + " + obj.getName() + " + Consts.DOUBLE_QUOTE + ">" + Consts.DOUBLE_QUOTE + ";");
						writeln("strTag += obj.getValue().replace(/&/g, " + Consts.DOUBLE_QUOTE + "&amp;" + Consts.DOUBLE_QUOTE + ").replace(/</g, " + Consts.DOUBLE_QUOTE + "&lt;" + Consts.DOUBLE_QUOTE + ").replace(/>/g, " + Consts.DOUBLE_QUOTE + "&gt;" + Consts.DOUBLE_QUOTE + ");");
						writeln("strTag += " + Consts.DOUBLE_QUOTE + "</" + Consts.DOUBLE_QUOTE + " + obj.getName() + " + Consts.DOUBLE_QUOTE + ">" + Consts.DOUBLE_QUOTE + ";");
					writeln("}");
				writeln("}");
				writeln("return strTag;");
			writeln("},");
		writeln("");
			writeln("// Retorna o nome da Tag sem o Namespace (Nome local)");
			writeln("getNodeLocalName: function (node) {");
				writeln("return node.nodeName.substring(node.nodeName.indexOf(" + Consts.DOUBLE_QUOTE + ":" + Consts.DOUBLE_QUOTE + ") + 1);");
			writeln("},");
		writeln("");
			writeln("//Retorna verdadeiro se o n� for folha");
			writeln("isLeafNode: function(node) {");
				writeln("return (node.childNodes.length == 1 && (node.childNodes[0].nodeType == 3 || node.childNodes[0].nodeType == 4));");
			writeln("},");
		writeln("");
			writeln("// verifica qual � a posi��o do no atual em rela��o aos irm�os mais velhos de mesmo nome");
			writeln("getSameNodesPosition: function(node) {");
				writeln("var result = 0;");
				writeln("while (Utils.samePreviousSibling(node)) {");
					writeln("node = node.previousSibling;");
					writeln("result++;");
				writeln("}");
				writeln("return result;");
			writeln("},");
		writeln("");
			writeln("// Constr�i um objeto do tipo OutParam se baseando na �rvore de n�s");
			writeln("nodeToObject: function(node, outParam) {");
				writeln("if (node == null) {");
					writeln("return null;");
				writeln("}");
				writeln("// Se o n� for do tipo texto, ent�o retorna seu valor final");
				writeln("if (node.nodeType == 3 || node.nodeType == 4) {");
					writeln("return Utils.getValue(node, outParam);");
				writeln("}");
				writeln("nodeLocalName = Utils.getNodeLocalName(node);");
				writeln("// Apenas continua se o nodeName existir no objeto outParam, caso contr�rio ocorre um erro");
				writeln("if (outParam.getName() == nodeLocalName) {");
					writeln("// Se for um n� folha retorna o m�todo recursivamente chamando o n� filho para o mesmo outParam");
					writeln("if (Utils.isLeafNode(node)) {");
						writeln("return Utils.nodeToObject(node.childNodes[0], outParam);");
					writeln("}");
					writeln("for(var i = 0; i < node.childNodes.length; i++) { ");
						writeln("var pos = Utils.getSameNodesPosition (node.childNodes[i]);");
						writeln("var p = Utils.nodeToObject(node.childNodes[i], outParam.getAnAttr(Utils.getNodeLocalName(node.childNodes[i]), pos));");
						writeln("outParam.setAnAttr(p, Utils.getNodeLocalName(node.childNodes[i]), pos);");
					writeln("}");
					writeln("return outParam;");
				writeln("}");
				writeln("throw new Error (500, " + Consts.DOUBLE_QUOTE + "Erro na convers�o de n�s para objetos" + Consts.DOUBLE_QUOTE + ");");
			writeln("},");
		writeln("");
			writeln("// Retorna um valor baseando-se no objeto que estaria destinado a receber o valor");
			writeln("getValue: function(node, outParam) {");
				writeln("var value = node.nodeValue;");
				writeln("if (outParam.getType() == " + Consts.DOUBLE_QUOTE + "Simple" + Consts.DOUBLE_QUOTE + ") {");
					writeln("outParam.setValue(value);");
					writeln("return outParam;");
				writeln("} else if (outParam.getType() == " + Consts.DOUBLE_QUOTE + "Complex" + Consts.DOUBLE_QUOTE + ") {");
					writeln("throw new Error (500, " + Consts.DOUBLE_QUOTE + "Não é possível retorna o valor de um ComplexType" + Consts.DOUBLE_QUOTE + ")");
				writeln("}");
//				writeln("switch(outParam.getType()) {");
//					writeln("case " + Consts.DOUBLE_QUOTE + "Simple" + Consts.DOUBLE_QUOTE + ":");
//						writeln("(value != null) ? outParam.setValue(value + " + Consts.DOUBLE_QUOTE + Consts.DOUBLE_QUOTE + ") : outParam.setValue(" + Consts.DOUBLE_QUOTE + Consts.DOUBLE_QUOTE + ");");
//						writeln("return outParam;");
//					writeln("case " + Consts.DOUBLE_QUOTE + "Boolean" + Consts.DOUBLE_QUOTE + ":");
//						writeln("outParam.setValue((value + " + Consts.DOUBLE_QUOTE + Consts.DOUBLE_QUOTE + ").toLowerCase() == " + Consts.DOUBLE_QUOTE + "true" + Consts.DOUBLE_QUOTE + ");");
//						writeln("return outParam;");
//					writeln("case " + Consts.DOUBLE_QUOTE + "Number" + Consts.DOUBLE_QUOTE + ":");
//						writeln("if (value % 2 == 0 || value % 2 == 1 || value % 2 == -1) {");
//							writeln("outParam.setValue(parseInt(value + " + Consts.DOUBLE_QUOTE + Consts.DOUBLE_QUOTE + ", 10));");
//						writeln("} else {");
//							writeln("(value != null) ? outParam.setValue(parseFloat(value + " + Consts.DOUBLE_QUOTE + Consts.DOUBLE_QUOTE + ")) : outParam.setValue(0);");
//						writeln("}");
//						writeln("return outParam;");
//					writeln("default:");
//						writeln("throw new Error (500, " + Consts.DOUBLE_QUOTE + "Tipo do objeto mal formatado." + Consts.DOUBLE_QUOTE + ");");
//				writeln("}");
			writeln("},");
		writeln("");
			writeln("// Retorna o n� que possui o local name passado por par�metro, caso exista mais de um os namespaces seriam necess�rios e ocasionar� em um erro, visto que o IE n�o trabalha com namespaces.");
			writeln("getNodeByLocalName: function (nodeList, localName) {");
				writeln("var qtd = 0;");
				writeln("for (i=0; i < nodeList.length; i++) {");
					writeln("if (nodeList[i].nodeName.substring(nodeList[i].nodeName.indexOf(" + Consts.DOUBLE_QUOTE + ":" + Consts.DOUBLE_QUOTE + ") + 1).match(localName)) {");
						writeln("qtd = qtd + 1;");
						writeln("node = nodeList[i];");
					writeln("}");
				writeln("}");
				writeln("if (qtd < 1) throw new Error(500, " + Consts.DOUBLE_QUOTE + "Tentativa de recuperar um n� inexistente." + Consts.DOUBLE_QUOTE + ");");
				writeln("if (qtd > 1) throw new Error(500, " + Consts.DOUBLE_QUOTE + "Existe mais de um n� para este LocalName." + Consts.DOUBLE_QUOTE + ");");
				writeln("return node;");
			writeln("},");
		writeln("");
			writeln("// Ajuda a construir uma estrutura de N� a partir da um xml. getElementsByTagName n�o funciona no IE6 e anteriores.");
			writeln("getNode: function(xml, tag) {");
				writeln("try {");
					writeln("nodeList = xml.responseXML.selectNodes(" + Consts.DOUBLE_QUOTE + ".//*[local-name()=" + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + Consts.DOUBLE_QUOTE + "+ tag +" + Consts.DOUBLE_QUOTE + Consts.BACKSLASH + Consts.DOUBLE_QUOTE + "]" + Consts.DOUBLE_QUOTE + "); // ser� que funciona? falta testar");
					writeln("return Utils.getNodeByLocalName(nodeList, tag);");
				writeln("}");
				writeln("catch (ex) {}");
				writeln("nodeList = xml.responseXML.getElementsByTagName('*');");
				writeln("return Utils.getNodeByLocalName(nodeList, tag);");
			writeln("},");
		writeln("");
			writeln("//Testa os parsers para uma compatibilidade com diversos browsers.");
			writeln("getXmlHttp: function() { ");
				writeln("try {");
					writeln("if (window.XMLHttpRequest) {");
						writeln("var req = new XMLHttpRequest();");
						writeln("if (req.readyState == null) {");
							writeln("req.readyState = 1;");
							writeln("req.addEventListener(" + Consts.DOUBLE_QUOTE + "load" + Consts.DOUBLE_QUOTE + ", function() {");												
								writeln("req.readyState = 4;");
								writeln("if (typeof req.onreadystatechange == " + Consts.DOUBLE_QUOTE + "function" + Consts.DOUBLE_QUOTE + ") {");
									writeln("req.onreadystatechange();");
								writeln("}");
								writeln("},");
								writeln("false);");
						writeln("}");
						writeln("return req;");
					writeln("}");
					writeln("if (window.ActiveXObject) {");
						writeln("var parsers = [" + Consts.DOUBLE_QUOTE + "Msxml2.XMLHTTP.5.0" + Consts.DOUBLE_QUOTE + ", " + Consts.DOUBLE_QUOTE + "Msxml2.XMLHTTP.4.0" + Consts.DOUBLE_QUOTE + ", " + Consts.DOUBLE_QUOTE + "MSXML2.XMLHTTP.3.0" + Consts.DOUBLE_QUOTE + ", " + Consts.DOUBLE_QUOTE + "MSXML2.XMLHTTP" + Consts.DOUBLE_QUOTE + ", " + Consts.DOUBLE_QUOTE + "Microsoft.XMLHTTP" + Consts.DOUBLE_QUOTE + "];");
						writeln("for(var i = 0; i < parsers.length; i++) {");
							writeln("try {");
								writeln("return new ActiveXObject(parsers[i]);");
							writeln("}");
							writeln("catch (ex) {};");
						writeln("}");
						writeln("throw new Error(" + Consts.DOUBLE_QUOTE + "Imposs�vel encontrar um Xml parser instalado" + Consts.DOUBLE_QUOTE + ");");
					writeln("}");
				writeln("}");
				writeln("catch (ex) {}");
				writeln("throw new Error(" + Consts.DOUBLE_QUOTE + "Este navegador n�o suporta objectos XmlHttp" + Consts.DOUBLE_QUOTE + ");");
			writeln("}");
		writeln("}");

	}
	
	// PRIVATE
	
	Description fDescription;
	
	private void wsdl2JsElement (XmlSchemaElement element) {
		if (element.getSchemaTypeName() != null) {
			writeln("instance.addElement(new types." + element.getSchemaType().getName() + "("+ Consts.DOUBLE_QUOTE + element.getName() + Consts.DOUBLE_QUOTE + "));");
		} else {
			if (element.getRefName() != null) {
				//TODO analisar uma nova solução
//				writeln("instance.addElement(.getAnAttr(" + Consts.DOUBLE_QUOTE + element.getRefName().getLocalPart() + Consts.DOUBLE_QUOTE + "));");
			
//				ElementDeclaration[] allElementsDeclaration = fDescription.getElementDeclarations();
//				XmlSchemaElement newElement;
//				int i = 0;
//				boolean found = false;
//				while ((!found) && (i < allElementsDeclaration.length)) {
//					newElement = (XmlSchemaElement) allElementsDeclaration[i].getContent();
//					if (element.getRefName().equals(newElement.getQName())) {
//						found = true;
//						wsdl2JsElement(newElement);
//					}
//					i++;					                                                  
//				}
//				if (!found) {
//					ExceptionHandler.handle(new Exception("Existe uma referência a um element desconhecido."));
//				}
			} else {
				wsdl2JsTypeSN (element.getSchemaType(), element.getName());
			}
		}
	}
	
	private void wsdl2JsTypeSN (XmlSchemaType type, String elementName) {
		if (type instanceof XmlSchemaComplexType) {
			wsdl2JsComplexTypeSN((XmlSchemaComplexType) type, elementName);
		} else {
			if (type instanceof XmlSchemaSimpleType) {
				wsdl2JsSimpleTypeSN((XmlSchemaSimpleType) type, elementName);
			} else {
				ExceptionHandler.handle(new Exception("SchemaType não é ComplexType ou SimpleType"));
			}
		}
		writeln("");
	}
	
	private void wsdl2JsType (XmlSchemaType type) {
		if (type instanceof XmlSchemaComplexType) {
			wsdl2JsComplexType((XmlSchemaComplexType) type);
		} else {
			if (type instanceof XmlSchemaSimpleType) {
				wsdl2JsSimpleType((XmlSchemaSimpleType) type);
			} else {
				ExceptionHandler.handle(new Exception("SchemaType não é ComplexType ou SimpleType"));
			}
		}
		writeln("");
	}
	
	private void wsdl2JsComplexTypeSN (XmlSchemaComplexType complexType, String elementName) {
		writeln("instance.addElement(new function() {");
			writeln("var instance = {};");
			writeln("instance.inheritFrom = types.WsdlObject;");
			writeln("instance.inheritFrom(" + Consts.DOUBLE_QUOTE + elementName + Consts.DOUBLE_QUOTE + ", " + Consts.DOUBLE_QUOTE + "Complex" + Consts.DOUBLE_QUOTE + ",undefined);");
			insideComplexType(complexType);
			writeln("return instance;");
		writeln("});");
	}
	
	private void wsdl2JsComplexType (XmlSchemaComplexType complexType) {
		writeln("instance." + complexType.getName() + " = function(_name) {");
			writeln("var instance = {};");
			writeln("instance.inheritFrom = types.WsdlObject;");
			writeln("instance.inheritFrom(_name, " + Consts.DOUBLE_QUOTE + "Complex" + Consts.DOUBLE_QUOTE + ",undefined);");
			insideComplexType(complexType);
			writeln("return instance;");
		writeln("}");
	}
	
	private void wsdl2JsSimpleTypeSN (XmlSchemaSimpleType simpleType, String elementName) {
		if (simpleType.getContent() instanceof XmlSchemaSimpleTypeRestriction) {
			XmlSchemaSimpleTypeRestriction simpleTypeRestriction = (XmlSchemaSimpleTypeRestriction) simpleType.getContent();
			writeln("instance.addElement(new types." + simpleTypeRestriction.getBaseTypeName().getLocalPart() + "(" + Consts.DOUBLE_QUOTE + elementName + Consts.DOUBLE_QUOTE + "));");
		}
	}
	
	private void wsdl2JsSimpleType (XmlSchemaSimpleType simpleType) {
	writeln("instance." + simpleType.getName() + " = function(_name) {");
		writeln("var instance = {};");
		writeln("instance.inheritFrom = types.WsdlObject;");
		writeln("instance.inheritFrom(_name, " + Consts.DOUBLE_QUOTE + "Simple" + Consts.DOUBLE_QUOTE + ",undefined);");
		writeln("return instance;");
	writeln("}");		
	}
	
	@SuppressWarnings("unchecked")
	private void insideComplexType (XmlSchemaComplexType complexType) {
		if ((complexType.getParticle() instanceof XmlSchemaSequence) || (complexType.getParticle() instanceof XmlSchemaAll)) {
			XmlSchemaGroupBase groupBase;
			if (complexType.getParticle() instanceof XmlSchemaSequence)
				groupBase = (XmlSchemaSequence) complexType.getParticle();
			else
				groupBase = (XmlSchemaAll) complexType.getParticle();
			XmlSchemaObjectCollection items =  groupBase.getItems();
			for (Iterator i = items.getIterator(); i.hasNext();) {
				Object item = i.next();
				if (item instanceof XmlSchemaElement) {
					wsdl2JsElement ((XmlSchemaElement) item);
				} else {
					//TODO Tratar
					//System.out.println("***********Tem que tratar quando o item dentro da sequence não é Element***************");
				}
			}
		} else {
		//TODO tratar
		//System.out.println("********************Vai ter que tratar quando o particle não é uma sequence******************");
		}
	}
}